///////////////////////////////////////////////////////////////

//Fws=48k
//Fsck=3.072M
//Fmck=24.576M

module Equalizer
(
    (*mark_debug ="true"*)input   logic           clk,
    (*mark_debug ="true"*)input   logic           rst_n,
    (*mark_debug ="true"*)input   logic   [9:0]   low_pass_coefficient,
    (*mark_debug ="true"*)input   logic   [9:0]   band_pass_coefficient,
    (*mark_debug ="true"*)input   logic   [9:0]   high_pass_coefficient,
    (*mark_debug ="true"*)output  wire            mck,
    (*mark_debug ="true"*)output  wire            sck,
    (*mark_debug ="true"*)output  wire            ws0,
                          output  wire            ws1,
    (*mark_debug ="true"*)input   wire            ad_sd,
    (*mark_debug ="true"*)output  wire            da_sd
);

    assign mck = clk;
    assign ws1 = ws0;
    wire               data_valid;
    wire               sck_fall, frame_sync;
    wire signed [31:0] data[2];
    reg  signed [31:0] data_filted[2];
    reg                filter_clk;      
    wire signed [31:0] high_pass_filted_l;
    wire signed [31:0] band_pass_filted_l;
    wire signed [31:0] low_pass_filted_l;
    wire signed [31:0] high_pass_filted_r;
    wire signed [31:0] band_pass_filted_r;
    wire signed [31:0] low_pass_filted_r;

    always_ff@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
        begin
            data_filted[0] <= 32'b0;
            data_filted[1] <= 32'b0;
        end
        else 
        if(data_valid)
        begin
            data_filted[0] <= 
            high_pass_filted_l*high_pass_coefficient>>>8+
            band_pass_filted_l*band_pass_coefficient>>>8+
            low_pass_filted_l*low_pass_coefficient>>>8;
            data_filted[1] <= 
            high_pass_filted_r*high_pass_coefficient>>>8+
            band_pass_filted_r*band_pass_coefficient>>>8+
            low_pass_filted_r*low_pass_coefficient>>>8;
        end        
    end


    IIR #(32,1) 
    u_IIR_L(
        .data                   (data[0]            ),
        .clk                    (data_valid         ),
        .rst                    (!rst_n             ),
        .high_pass_filted       (high_pass_filted_l ),
        .band_pass_filted       (band_pass_filted_l ),
        .low_pass_filted        (low_pass_filted_l  )
    );
    IIR #(32,1) 
    u_IIR_R(
        .data                   (data[1]            ),
        .clk                    (data_valid         ),
        .rst                    (!rst_n             ),
        .high_pass_filted       (high_pass_filted_r ),
        .band_pass_filted       (band_pass_filted_r ),
        .low_pass_filted        (low_pass_filted_r  )
    );

     IisClkGen #(.SCK_TO_WS(64), .MCK_TO_SCK(8))
     theIisClkGen(
     .mck(mck), .sck(sck), .ws(ws0),
     .sck_fall(sck_fall), .frame_sync(frame_sync)
     );

     IisReceiver#()
     theIisRecvr(
     .mck(mck), .iis_sck(sck), .iis_ws(ws0), .iis_sd(ad_sd),
     .data(data), .data_valid(data_valid)
     ); 
    
     IisTransmitter#() 
     theIisTrans(
     .mck(mck), .sck_fall(sck_fall), .frame_sync(frame_sync),
     .data(data_filted), .data_rd(), .iis_sd(da_sd)
     );

endmodule


module IisClkGen #(
    parameter SCK_TO_WS = 64,
    parameter MCK_TO_SCK = 8
)(
    input wire mck,
    output logic sck,
    output logic ws,
    output logic sck_fall,
    output logic frame_sync
);
    localparam SCKW = $clog2(MCK_TO_SCK);
    localparam WSW = $clog2(SCK_TO_WS);
    logic [SCKW - 1 : 0] cnt_sck;
    logic cnt_sck_co;
    logic [WSW - 1 : 0] cnt_ws;
    always_ff@(posedge mck) sck <= cnt_sck >= SCKW'(MCK_TO_SCK / 2);
    always_ff@(posedge mck) ws <= cnt_ws >= WSW'(SCK_TO_WS / 2);
    always_ff@(posedge mck) sck_fall <= cnt_sck_co;
    Counter #(MCK_TO_SCK) cntSck(
        mck, 1'b0, 1'b1, cnt_sck, cnt_sck_co);
    Counter #(SCK_TO_WS) cntWs(
        mck, 1'b0, cnt_sck_co, cnt_ws, );
    assign frame_sync = (cnt_ws == 0) && cnt_sck_co;
endmodule



module IisTransmitter (
    input wire mck,
    input wire sck_fall,
    input wire frame_sync,
    input wire signed [31:0] data[2], //data[0]: left; data[1]: right
    output logic data_rd,
    output logic iis_sd
);
    assign data_rd = frame_sync;
    logic data_rd_dly;
    logic [63:0] shift_reg;
    always_ff@(posedge mck) data_rd_dly <= data_rd;
    always_ff@(posedge mck) begin
        if(data_rd_dly) shift_reg <= {data[0], data[1]};
        else if(sck_fall) shift_reg <= {shift_reg[62:0], 1'b0};
    end
    assign iis_sd = shift_reg[63];
endmodule

module IisReceiver (
    input wire mck, iis_sck, iis_ws, iis_sd,
    output logic signed [31:0] data[2],
    output logic data_valid    
);
    logic sck_rising, sck_reg, ws_falling, sd_reg;
    Rising2En #(2) sckRising(mck, iis_sck, sck_rising, sck_reg);
    Falling2En #(2) wsFalling(mck, iis_ws, ws_falling, );
    Rising2En #(2) sdSync(mck, iis_sd, , sd_reg);
    logic [7:0] bit_cnt;
    Counter #(256) bitCnt(mck, ws_falling, sck_rising, bit_cnt, );
    logic frame_end;
    always_ff@(posedge mck) frame_end <= (bit_cnt == 8'd0) && sck_rising;
    always_ff@(posedge mck) data_valid <= frame_end;
    logic [63:0] shift_reg;
    always_ff@(posedge mck) begin
        if(frame_end) {data[0], data[1]} <= shift_reg;
        else if(sck_rising) shift_reg <= {shift_reg[62:0], sd_reg};
    end
endmodule

module IIR #(
    parameter DW = 10, EW = 4
)
(
    input  wire     signed  [31:0]      data,
    input  wire                         clk,
    input  wire                         rst,
    input  wire                         en,
    output reg      signed  [31:0]      high_pass_filted,
    output reg      signed  [31:0]      band_pass_filted,
    output reg      signed  [31:0]      low_pass_filted
    );
        localparam W = EW + DW;


//    IIR2nd #(W,DW-1,0.858294933047827224292802839045180007815,
//    {1,-1,0},{-0.716589866095654337563303215574705973268,0}) 
//    HIGH_PASS_IIR (clk,rst,en,data,high_pass_filted);
    IIR2nd #(W,DW-1,0.858294933047827224292802839045180007815,
    '{1,-1,0},'{-0.7165898,0}) 
    HIGH_PASS_IIR (clk,rst,en,data,high_pass_filted);

    IIR2nd #(W,DW-1,0.121509903764748128374151292518945410848,
    '{1,0,-1},'{-1.741853638166788398322637476667296141386, 0.756980192470503743251697414962109178305}) 
    BAND_PASS_IIR (clk,rst,en,data,band_pass_filted);

    IIR2nd #(W,DW-1,0.025517716642560115541948917439185606781,
    '{1,1,0},'{-0.948964566714879831366147300286684185266,0}) 
    LOW_PASS_IIR (clk,rst,en,data,low_pass_filted);


endmodule

module IIR2nd #(
    parameter DW = 32, FW = 0,
    parameter real GAIN, real NUM[3], real DEN[2]
)(
    input wire clk, rst, en,
    input wire signed [DW-1 : 0] in,    // Q(DW-FW).FW
    output logic signed [DW-1 : 0] out  // Q(DW-FW).FW
);

    function automatic [DW - 1 : 0]mul(input signed  [DW - 1 : 0] x, y); 
    begin 
        mul = (((DW-FW)+(DW-FW)+(FW)+(FW))  '(x)   * ((DW-FW)+(DW-FW)+(FW)+(FW))'(y)  ) >>> ((FW)+(FW)-(FW));
    end 
    endfunction


    wire signed [DW-1:0] n0 = (NUM[0] * 2.0**FW);
    wire signed [DW-1:0] n1 = (NUM[1] * 2.0**FW);
    wire signed [DW-1:0] n2 = (NUM[2] * 2.0**FW);
    wire signed [DW-1:0] d1 = (DEN[0] * 2.0**FW);
    wire signed [DW-1:0] d2 = (DEN[1] * 2.0**FW);
    wire signed [DW-1:0] g  = (GAIN   * 2.0**FW);
    logic signed [DW-1:0] z1, z0;
    wire signed [DW-1:0] pn0 = mul(in, n0);
    wire signed [DW-1:0] pn1 = mul(in, n1);
    wire signed [DW-1:0] pn2 = mul(in, n2);
    wire signed [DW-1:0] o = pn0 + z0;
    wire signed [DW-1:0] pd1 = mul(o, d1);
    wire signed [DW-1:0] pd2 = mul(o, d2);

    always_ff@(posedge clk) begin
        if(rst) begin z0 <= '0; z1 <= '0; out <= '0; end
        else if(en) begin
            z1 <= pn2 - pd2;
            z0 <= pn1 - pd1 + z1;
            out <= mul(o, g);
        end
    end
endmodule


module Counter #(
    parameter M = 100
)(
    input wire clk, rst, en,
    output logic [$clog2(M) - 1 : 0] cnt,
    output logic co
);
    assign co = en & (cnt == M - 1);
    always_ff@(posedge clk) begin
        if(rst) cnt <= '0;
        else if(en) begin
            if(cnt < M - 1) cnt <= cnt + 1'b1;
            else cnt <= '0;
        end
    end
endmodule


module Rising2En #( parameter SYNC_STG = 1 )(
    input wire clk, in,
    output logic en, out
);
    logic [SYNC_STG : 0] dly;
    always_ff@(posedge clk) begin
        dly <= {dly[SYNC_STG - 1 : 0], in};    
    end
    assign en = (SYNC_STG ? dly[SYNC_STG -: 2] : {dly, in}) == 2'b01;
    assign out = dly[SYNC_STG];
endmodule

module Falling2En #( parameter SYNC_STG = 1 )(
    input wire clk, in,
    output logic en, out
);
    logic [SYNC_STG : 0] dly;
    always_ff@(posedge clk) begin
        dly <= {dly[SYNC_STG - 1 : 0], in};    
    end
    assign en = (SYNC_STG ? dly[SYNC_STG -: 2] : {dly, in}) == 2'b10;
    assign out = dly[SYNC_STG];
endmodule